package com.bsj.pay.config;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * @author chhuean
 * @version 1.0
 * @date 2022/8/8
 **/
public class LoggingInterceptor implements ClientHttpRequestInterceptor {
    private static final Logger log = LogManager.getLogger(LoggingInterceptor.class);

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        displayRequest(request, body);
        ClientHttpResponse response = execution.execute(request, body);
        // 包装代理一下
        response = (ClientHttpResponse) Proxy.newProxyInstance(response.getClass().getClassLoader(), new Class[]{ClientHttpResponse.class}, new ClientHttpResponseHandler(response));
        displayResponse(response);
        return response;
    }

    private void displayResponse(ClientHttpResponse response) throws IOException {
        StringBuilder inputStringBuilder = new StringBuilder();
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
            String line = bufferedReader.readLine();
            while (line != null) {
                inputStringBuilder.append(line);
                inputStringBuilder.append('\n');
                line = bufferedReader.readLine();
            }
        }
        log.debug("============================response begin==========================================");
        log.debug("Status code  : {}", response.getStatusCode());
        log.debug("Status text  : {}", response.getStatusText());
        log.debug("Headers      : {}", response.getHeaders());
        log.debug("Response body: {}", inputStringBuilder.toString());
        log.debug("=======================response end=================================================");
    }

    private void displayRequest(HttpRequest request, byte[] body) throws IOException {
        log.debug("===========================request begin================================================");
        log.debug("URI         : {}", request.getURI());
        log.debug("Method      : {}", request.getMethod());
        log.debug("Headers     : {}", request.getHeaders() );
        log.debug("Request body: {}", new String(body, "UTF-8"));
        log.debug("==========================request end================================================");
    }

    private static class ClientHttpResponseHandler implements InvocationHandler {
        private static final String methodName = "getBody";
        private ClientHttpResponse clientHttpResponse;
        private byte[] body;

        public ClientHttpResponseHandler(ClientHttpResponse clientHttpResponse) {
            this.clientHttpResponse = clientHttpResponse;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (methodName.equals(method.getName())) {
                if (Objects.isNull(this.body)) {
                    this.body = StreamUtils.copyToByteArray(this.clientHttpResponse.getBody());
                }
                return new ByteArrayInputStream(this.body == null ? new byte[0] : this.body);
            }

            return method.invoke(this.clientHttpResponse, args);
        }
    }
}
